#include <cstdio>
#include <glad/glad.h>
#include <SDL2/SDL.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/euler_angles.hpp>
#include "shader/RobotShader.h"
#include "shader/LightShader.h"
#include "Mesh.h"
#include "XB4.h"
#include "UI.h"
#include <Singleton.h>
#include "GlobalData.h"
#include "SkyBox.h"
#include "CoordDraw.h"
#include <random>

using namespace std;


auto printMat4 = [](const glm::mat4& mat)
{
    stringstream ss;
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            ss << mat[i][j];
            if (j != 3 || i != 3)
                ss << " ";
        }
        if (i != 3)
            ss << " ";
    }
    return ss.str();
};

int main(int argc, char** argv)
{
    const int windowWidth = 1280;
    const int windowHeight = 1024;
    // 设置 SDL 版本和信息
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_Init(SDL_INIT_EVERYTHING);

    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
        return -1;
    }
    atexit(SDL_Quit);
    SDL_Window* window = SDL_CreateWindow("SDL 窗口", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
    if(window == nullptr)
    {
        printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
        return -1;
    }
    SDL_GLContext gl_context = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, gl_context);
    SDL_GL_SetSwapInterval(1);
    //注册OpenGL函数指针
    if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
    {
        printf("Failed to Load GL Loader for glad!");
        return -1;
    }

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    SDL_Event event;

    auto& proj = Singleton<GlobalData>::Instance().ProjMatrix;
////     正交投影
//    proj = glm::ortho(-(float)windowWidth / 600.0f,(float)windowWidth / 600.0f,
//                                 -(float)windowHeight / 600.0f,(float)windowHeight / 600.0f,
//                                 0.1f, 100.0f);
    // 透视投影
    proj = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeight, 0.1f, 500.0f);

    auto& view = Singleton<GlobalData>::Instance().ViewMatrix;

    auto robot = XB4();
    auto& jointAngle = Singleton<GlobalData>::Instance().JointAngle;
    auto& jointAngleMax = Singleton<GlobalData>::Instance().JointAngleMax;
    auto& jointAngleMin = Singleton<GlobalData>::Instance().JointAngleMin;
    auto& jointAngleDirty = Singleton<GlobalData>::Instance().JointAngleDirty;

    jointAngleMax = { 0, 170, 54, 54, 170, 119, 180 };
    jointAngleMin = { 0, -170, -170, -194, -170, -119, -180 };
    jointAngle.resize(7);
    for (int i = 1; i < 7; ++i)
        jointAngle[i] = robot.GetJointPara(i);

    auto& drawSkybox = Singleton<GlobalData>::Instance().DrawSkybox;
    auto skyBox = SkyBox("bits/skybox");

    auto coordDraw = CoordDraw();

    vector<shared_ptr<Mesh>> links;
    links.reserve(7);

    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link1.obj"));
    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link2.obj"));
    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link3.obj"));
    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link4.obj"));
    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link5.obj"));
    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link6.obj"));
    links.emplace_back(Mesh::LoadMeshFromFile("bits/Robot/xb4_link7.obj"));

    auto light = Mesh::LoadMeshFromFile("bits/Light/light.gltf");

    for (int i = 0; i < 7; ++i) {
        links[i]->SetOffsetMatrix(glm::inverse(robot.GetJointTransform(i)));
    }

    light->SetOffsetMatrix(glm::mat4(1.0f));
    light->SetModelMatrix(glm::mat4(1.0f));

    auto& lightColor = Singleton<GlobalData>::Instance().EnvLightColor;
    glm::vec3 lightPos = glm::vec3(4.00f, 8.00f, 2.0f);

    glm::vec3 camPos = glm::vec3(0.0f, 5.0f, 20.0f);
    glm::vec3 camRot = glm::vec3(0.0f, 0.0f, 0.0f);


    RobotShader robotShader;
    LightShader lightShader;

    int counter = 0;

    auto worldCamRot = glm::vec3(0.0f);

    auto camPos_d = camPos;

    static auto getRandomColor = []()
    {
        static random_device rd;
        static mt19937 mt(rd());
        static uniform_real_distribution<float> dist(0, 1);
        return glm::vec3(dist(mt), dist(mt), dist(mt));
    };

    auto& robotLinkColors = Singleton<GlobalData>::Instance().RobotLinkColors;
    robotLinkColors.reserve(links.size());
    for (int i = 0; i < links.size(); ++i)
    {
        robotLinkColors.emplace_back(getRandomColor());
    }


    robotShader.UseShader();
    robotShader.setFloat("material.ambientStrength", 0.3f);
    robotShader.setFloat("material.diffuseStrength", 0.7f);
    robotShader.setFloat("material.specularStrength", 1.0f);
    robotShader.setFloat("material.shininess", 32.0f);
    robotShader.setMat4("projMatrix", proj);

    lightShader.UseShader();
    lightShader.setMat4("projMatrix", proj);

    auto d = 1.0f;

    auto ui = UI(window, gl_context);
    auto& runFlag = Singleton<GlobalData>::Instance().RunFlag;
    runFlag = true;

    //渲染循环
    while(runFlag)
    {
        //处理键盘事件
        while (SDL_PollEvent(&event))
        {
            ui.HandleInput(event);
            if (event.type == SDL_QUIT)
                runFlag = false;

            if (event.type == SDL_KEYDOWN)
            {
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    runFlag = false;
                if (event.key.keysym.sym == SDLK_i)
                    worldCamRot += glm::vec3(0.05f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_k)
                    worldCamRot += glm::vec3(-0.05f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_l)
                    worldCamRot += glm::vec3(0.0f, 0.05f, 0.0f);
                if (event.key.keysym.sym == SDLK_j)
                    worldCamRot += glm::vec3(0.0f, -0.05f, 0.0f);
                if (event.key.keysym.sym == SDLK_u)
                    worldCamRot += glm::vec3(0.0f, 0.0f, 0.05f);
                if (event.key.keysym.sym == SDLK_o)
                    worldCamRot += glm::vec3(0.0f, 0.0f, -0.05f);

                if (event.key.keysym.sym == SDLK_w)
                    camPos += glm::vec3(0.0f, 0.05f, 0.0f);
                if (event.key.keysym.sym == SDLK_s)
                    camPos += glm::vec3(0.0f, -0.05f, 0.0f);
                if (event.key.keysym.sym == SDLK_a)
                    camPos += glm::vec3(-0.05f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_d)
                    camPos += glm::vec3(0.05f, 0.0f, 0.0f);
                if (event.key.keysym.sym == SDLK_q)
                    camPos += glm::vec3(0.0f, 0.0f, 0.05f);
                if (event.key.keysym.sym == SDLK_e)
                    camPos += glm::vec3(0.0f, 0.0f, -0.05f);

                if(event.key.keysym.sym == SDLK_SPACE)
                {
                    camPos = camPos_d;
                    worldCamRot = glm::vec3(0.0f, 0.0f, 0.0f);
                }
            }
        }

        ui.Logic();

        if (jointAngleDirty)
        {
            for (int i = 1; i < 7; ++i)
                robot.SetJointPara(i, jointAngle[i]);
            jointAngleDirty = false;
        }

        auto& bc = Singleton<GlobalData>::Instance().RenderBackgroundColor;
        glClearColor(bc.x, bc.y, bc.z, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        view = glm::mat4(1.0f);
        glm::mat4 worldCamRotM = glm::eulerAngleXYZ(worldCamRot.x, worldCamRot.y, worldCamRot.z);
        view = view * worldCamRotM;
        view = glm::translate(view, camPos);
        view = view * glm::eulerAngleXYZ(camRot.x, camRot.y, camRot.z);
        view = glm::inverse(view);

        if (drawSkybox)
            skyBox.Render();

        coordDraw.Render();

        counter++;

        robotShader.UseShader();

        robotShader.setVec3("dirLight.direction", -lightPos);
        robotShader.setVec3("dirLight.color", lightColor * 0.0f);

        robotShader.setVec3("pointLight.position", lightPos);
        robotShader.setVec3("pointLight.color", lightColor);
        robotShader.setFloat("pointLight.constant", 1.0f);
        robotShader.setFloat("pointLight.linear", 0.009f);
        robotShader.setFloat("pointLight.quadratic", 0.0032f);

        robotShader.setMat4("viewMatrix", view);
        robotShader.setVec3("viewPos", glm::mat3(worldCamRotM) * camPos);

        {
            glm::mat4 model(1.0f);
//            model = glm::rotate(model, glm::radians(d + 20.0f), glm::vec3(0.5, 1.0, 0.5));
            model = glm::scale(model, glm::vec3(0.01f));
            for (int i = 0; i < links.size(); ++i) {
                robotShader.setVec3("ModelColor", robotLinkColors[i]);
                links[i]->SetModelMatrix(robot.GetJointTransform(i));
                links[i]->SetWorldMatrix(model);
                links[i]->Draw(robotShader);
            }
        }

        lightShader.UseShader();
        lightShader.setVec3("PointLightColor", lightColor);
        lightShader.setMat4("viewMatrix", view);
        {
            glm::mat4 model(1.0f);
            model = glm::translate(model, lightPos);
            model = glm::scale(model, glm::vec3(0.25f));
            light->SetWorldMatrix(model);
            light->Draw(lightShader);
        }

        ui.Render();

        SDL_GL_SwapWindow(window);
        SDL_Delay(16);
    }

    SDL_DestroyWindow(window);
    return 0;
}
